# -*- coding: utf-8 -*-
"""
验证码生成服务

提供验证码相关功能：
- 生成图形验证码
- 验证码图片编码
- 验证码存储和验证
- 防暴力破解机制
"""

import random
import string
import base64
import io
import logging
from typing import Tuple, Optional
from PIL import Image, ImageDraw, ImageFont
from ..core.redis_client import get_redis
from ..core.config import get_settings

logger = logging.getLogger(__name__)


class CaptchaService:
    """验证码服务"""
    
    def __init__(self):
        """初始化验证码服务"""
        self.redis_client = get_redis()
        self.settings = get_settings()
        
        # 验证码配置
        self.width = 120
        self.height = 50
        self.length = 4
        self.font_size = 36
        
        # 字符集（排除易混淆字符）
        self.charset = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'
        
        # 颜色配置
        self.bg_color = (240, 240, 240)  # 浅灰背景
        self.text_colors = [
            (50, 50, 50),    # 深灰
            (80, 80, 80),    # 灰色
            (60, 60, 60),    # 中灰
        ]
        self.noise_color = (200, 200, 200)  # 噪点颜色
        self.line_color = (180, 180, 180)   # 干扰线颜色
    
    def generate_code(self) -> str:
        """生成验证码字符串"""
        return ''.join(random.choices(self.charset, k=self.length))
    
    def create_image(self, code: str) -> Image.Image:
        """创建验证码图片"""
        # 创建图片
        image = Image.new('RGB', (self.width, self.height), self.bg_color)
        draw = ImageDraw.Draw(image)
        
        try:
            # 尝试加载字体（如果系统有的话）
            font = ImageFont.truetype("arial.ttf", self.font_size)
        except (OSError, IOError):
            # 使用默认字体
            try:
                font = ImageFont.load_default()
            except:
                font = None
        
        # 绘制验证码文字
        self._draw_text(draw, code, font)
        
        # 添加干扰元素
        self._add_noise(draw)
        self._add_lines(draw)
        
        return image
    
    def _draw_text(self, draw: ImageDraw.Draw, code: str, font) -> None:
        """绘制验证码文字"""
        char_width = self.width // self.length
        
        for i, char in enumerate(code):
            # 随机颜色
            color = random.choice(self.text_colors)
            
            # 计算字符位置（添加随机偏移）
            x = char_width * i + random.randint(5, 15)
            y = random.randint(2, 8)
            
            # 绘制字符
            if font:
                draw.text((x, y), char, fill=color, font=font)
            else:
                # 如果没有字体，使用默认方式
                draw.text((x, y), char, fill=color)
    
    def _add_noise(self, draw: ImageDraw.Draw) -> None:
        """添加噪点"""
        for _ in range(50):
            x = random.randint(0, self.width - 1)
            y = random.randint(0, self.height - 1)
            draw.point((x, y), fill=self.noise_color)
    
    def _add_lines(self, draw: ImageDraw.Draw) -> None:
        """添加干扰线"""
        for _ in range(3):
            x1 = random.randint(0, self.width // 3)
            y1 = random.randint(0, self.height)
            x2 = random.randint(2 * self.width // 3, self.width)
            y2 = random.randint(0, self.height)
            draw.line([(x1, y1), (x2, y2)], fill=self.line_color, width=1)
    
    def image_to_base64(self, image: Image.Image) -> str:
        """将图片转换为Base64字符串"""
        buffer = io.BytesIO()
        image.save(buffer, format='PNG')
        buffer.seek(0)
        
        img_data = buffer.getvalue()
        base64_str = base64.b64encode(img_data).decode('utf-8')
        
        return f"data:image/png;base64,{base64_str}"
    
    def generate_captcha(self, session_id: str) -> Tuple[str, str]:
        """
        生成验证码
        
        Args:
            session_id: 会话ID
            
        Returns:
            Tuple[str, str]: (base64图片, 验证码答案)
        """
        try:
            # 生成验证码
            code = self.generate_code()
            
            # 创建图片
            image = self.create_image(code)
            
            # 转换为Base64
            base64_image = self.image_to_base64(image)
            
            # 存储到Redis
            success = self.redis_client.store_captcha(
                session_id, 
                code, 
                ttl=self.settings.CAPTCHA_EXPIRE_SECONDS
            )
            
            if not success:
                logger.error(f"验证码存储失败: {session_id}")
                raise Exception("验证码存储失败")
            
            logger.info(f"验证码生成成功: {session_id}")
            return base64_image, code
            
        except Exception as e:
            logger.error(f"验证码生成失败: {e}")
            raise
    
    def verify_captcha(self, session_id: str, user_input: str) -> bool:
        """
        验证验证码
        
        Args:
            session_id: 会话ID
            user_input: 用户输入的验证码
            
        Returns:
            bool: 验证是否成功
        """
        try:
            if not user_input or len(user_input) != self.length:
                logger.warning(f"验证码格式错误: {session_id}")
                return False
            
            return self.redis_client.verify_captcha(session_id, user_input)
            
        except Exception as e:
            logger.error(f"验证码验证异常: {e}")
            return False
    
    def is_captcha_required(self, username: str) -> bool:
        """
        判断是否需要显示验证码
        
        Args:
            username: 用户名
            
        Returns:
            bool: 是否需要显示验证码
        """
        return self.redis_client.is_captcha_required(
            username, 
            threshold=self.settings.LOGIN_FAIL_THRESHOLD
        )
    
    def increment_login_fails(self, username: str) -> int:
        """
        增加登录失败计数
        
        Args:
            username: 用户名
            
        Returns:
            int: 当前失败次数
        """
        return self.redis_client.increment_login_fails(
            username, 
            ttl=self.settings.LOGIN_FAIL_LOCK_SECONDS
        )
    
    def reset_login_fails(self, username: str) -> bool:
        """
        重置登录失败计数
        
        Args:
            username: 用户名
            
        Returns:
            bool: 是否重置成功
        """
        return self.redis_client.reset_login_fails(username)


# 全局验证码服务实例
captcha_service = CaptchaService()


# 便捷函数
def get_captcha_service() -> CaptchaService:
    """获取验证码服务实例"""
    return captcha_service